#include "guiterm.hpp"

ServerConnection::ServerConnection(BackendConnection *connection)
{
	this->inputSender = NULL;
	this->maxJobid = 0;
	this->maxTransferId = 0;
	this->lastUploader = 0;
	
	this->parentHost = NULL;
	this->parentJobid = 0;
	this->connection = connection;
	this->session = NULL;
	this->local = true;
}

ServerConnection::ServerConnection(ServerConnection *parent, int parentJobid, StreamSender *inputSender)
{
	this->inputSender = inputSender;
	this->maxJobid = 0;
	this->maxTransferId = 0;
	this->lastUploader = 0;
	
	this->parentHost = parent;
	this->parentJobid = parentJobid;
	this->connection = NULL;
	this->session = NULL;
	this->local = false;
	
	parentHost->children[parentJobid] = this;
}

bool ServerConnection::isLocal()
{
	return local;
}

void ServerConnection::idle()
{
	if(connection && !connection->isIdle())
		return;
	
	FileUploader *uploader = getAnyUploader();
	if(uploader) {
		uploader->doSend();
	} else {
		for(map<int, ServerConnection*>::iterator ii=children.begin(); ii!=children.end(); ii++)
			ii->second->idle();
	}
}

void ServerConnection::disconnected(bool error, string message)
{
	if(error) {
		session->reportError(message);
	} else {
		session->exit();
	}
}

void ServerConnection::attachSession(Session *session)
{
	this->session = session;
}

Session *ServerConnection::getSession()
{
	return session;
}

int ServerConnection::newJob()
{
	return ++maxJobid;
}

void ServerConnection::attachJob(RemoteJob *job)
{
	programs[job->getId()] = job;
}

RemoteJob *ServerConnection::getJob(int id)
{
	if(programs.find(id) != programs.end())
		return programs[id];
	else
		return NULL;
}

void ServerConnection::beginOutput(int job)
{
	if(programs.find(job) != programs.end()) {
		programs[job]->beginOutput();
	}
}

void ServerConnection::programOutput(int job, const char *data, int len)
{
	if(children.find(job) != children.end()) {
		children[job]->receivedData(len, data);
	} else if(programs.find(job) != programs.end()) {
		programs[job]->programOutput(data, len);
	}
}

void ServerConnection::outputFinished(int job)
{
	if(children.find(job) != children.end()) {
		// TODO: Handle disconnection
	} else {
		programs[job]->outputFinished();
	}
}

int ServerConnection::newTransferId()
{
	return ++maxTransferId;
}

void ServerConnection::attachDownloader(int id, FileRequest *downloader)
{
	downloaders[id] = downloader;
}

void ServerConnection::removeDownloader(int id)
{
	downloaders.erase(id);
}

FileRequest *ServerConnection::getDownloader(int id)
{
	if(downloaders.find(id) != downloaders.end())
		return downloaders[id];
	else
		return NULL;
}

void ServerConnection::attachUploader(FileUploader *uploader)
{
	uploaders.push_back(uploader);
	uploader->doSend();
}

void ServerConnection::removeUploader(FileUploader *uploader)
{
	for(vector<FileUploader*>::iterator ii=uploaders.begin(); ii!=uploaders.end(); ii++)
	{
		if(*ii == uploader) {
			uploaders.erase(ii);
			return;
		}
	}
}

FileUploader *ServerConnection::getAnyUploader()
{
	if(!uploaders.size())
		return NULL;
	
	if(lastUploader+1 < (int)uploaders.size())
		lastUploader++;
	else
		lastUploader = 0;
	
	return uploaders[lastUploader];
}

RPC_HANDLER(beginls)
{
	RemoteJob *job = getJob(host, params);
	string cmd = params->getString("cmd");
	
	FileView *view = new FileView(job);
	job->addFileView(view);
	view->beginRefresh((ServerConnection*)host, params);
}

RPC_HANDLER(dir)
{
	FileView *view = getPendingFileView(host, params);
	if(view) view->beginRefresh((ServerConnection*)host, params);
}

void ServerConnection::expectLsResponse(int id, FileView *view)
{
	pendingFileViews[id] = view;
}

FileView *ServerConnection::getPendingFileView(int id)
{
	if(pendingFileViews.find(id) != pendingFileViews.end()) {
		FileView *ret = pendingFileViews[id];
		pendingFileViews.erase(id);
		return ret;
	} else {
		return NULL;
	}
}

void ServerConnection::sendData(int len, const char *data)
{
	if(inputSender) {
		inputSender->send(len, data);
	} else if(connection) {
		connection->sendData(data, len);
	}
}



Session *getSession(RemoteHost *host, const RPCParams *params)
{
	return ((ServerConnection*)host)->getSession();
}

RemoteJob *getJob(RemoteHost *host, const RPCParams *params)
{
	int id = params->getInt("job");
	return ((ServerConnection*)host)->getJob(id);
}

FileRequest *getRequest(RemoteHost *host, const RPCParams *params)
{
	int id = params->getInt("id");
	return ((ServerConnection*)host)->getDownloader(id);
}

FileView *getPendingFileView(RemoteHost *host, const RPCParams *params)
{
	int id = params->getInt("id");
	return ((ServerConnection*)host)->getPendingFileView(id);
}


ProgramOutputHandler::ProgramOutputHandler(ServerConnection *host, const RPCParams *params)
	:StreamReceiver(host, params)
{
	this->host = host;
	this->job = params->getInt("job");
}

void ProgramOutputHandler::recv(int len, const char *data)
{
	host->programOutput(job, data, len);
}

void ProgramOutputHandler::onClose()
{
	host->outputFinished(job);
	deleteLater();
}

RPC_HANDLER(beginjob)
{
	((ServerConnection*)host)->beginOutput(params->getInt("job"));
	new ProgramOutputHandler((ServerConnection*)host, params);
}

RPC_HANDLER(jobstate)
{
	int jobid = params->getInt("job");
	string state = params->getString("state");
	
	RemoteJob *job = ((ServerConnection*)host)->getJob(jobid);
	job->setRunState(state);
}

RPC_HANDLER(output)
{
	((ServerConnection*)host)->beginOutput(params->getInt("job"));
	new ProgramOutputHandler((ServerConnection*)host, params);
}

